/**
  ETFAna project, Anyang Normal University && IMP-CAS
  \class ETFShm
  \brief shared memory class. Supposed to open and read existing shm for online
  ana purposes
  \author SUN Yazhou, asia.rabbit@163.com
  \since 2023-06-08
  \date 2023-06-08 last modified
  \attention
  changelog
  <table>
  <tr>  <th>Date         <th>Author      <th>Description                    </tr>
  <tr>  <td>2023-06-08   <td>Asia Sun    <td>file created                   </tr>
  </table>

  \copyright Copyright (c) 2021-2024 Anyang Normal U. && IMP-CAS with LGPLv3 LICENSE
*/

#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include "ETFShm.h"
#include "ETFMsg.h"
#include "online_shm.h"

ETFShm::ETFShm() : fBuffer(0), fDaqType(kVME), fIsCreate(false){}

// create posix shm with name \param isCreate whether to create shm if not exists
ETFShm::ETFShm(const string &name, DaqType daqType, bool isCreate)
: fBuffer(0), fDaqType(daqType), fIsCreate(isCreate){
  fName = name;
  int flags = O_RDWR;
  mode_t perms = 0;
  if(fIsCreate){
    flags |= O_CREAT; // | O_EXCL;
    perms |= S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
  } // end if

  int fd = shm_open(name.c_str(), flags, perms);
  if(-1 == fd) ETFMsg::Error("ETFshm", "Create: create - %s: %s", name.c_str(), strerror(errno));

  if(fIsCreate){
    size_t size = kPXI == fDaqType ? sizeof(online_shm_pxi) : sizeof(online_shm_vme);
    if(-1 == ftruncate(fd, size))
      ETFMsg::Error("ETFshm", "Create: ftruncate: - %s: %s", name.c_str(), strerror(errno));
  } // end if
  // get shared memory size
  if(-1 == fstat(fd, &fStat))
    ETFMsg::Error("ETFshm", "Create: fstat - %s: %s", name.c_str(), strerror(errno));
  // map the shared memory into virtual memory in user space
  fBuffer = mmap(nullptr, fStat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  if(MAP_FAILED == fBuffer)
    ETFMsg::Error("ETFshm", "Create: mmp - %s: %s", name.c_str(), strerror(errno));
  // fd is nolonger needed
  if(-1 == close(fd))
    ETFMsg::Error("ETFShm", "Create: close - %s: %s", name.c_str(), strerror(errno));
} // end ctor

ETFShm::~ETFShm(){
  // DO NOT USE DELETE HERE UNLESS YOU KNOW THAT YOU OWNS THE SHM //
  Close();
  if(fIsCreate) Delete();
} // end dtor

int ETFShm::Close(){
  if(!fBuffer) return -1;
  if(-1 == munmap(fBuffer, fStat.st_size)){
    ETFMsg::Error("ETFShm", "dtor %s: %s", fName.c_str(), strerror(errno));
    fBuffer = nullptr;
  } // end if
  return 0;
} // end member function Close

// delete the shm
int ETFShm::Delete(){
  if(-1 == shm_unlink(fName.c_str()))
    ETFMsg::Error("ETFShm", "dtor %s: %s", fName.c_str(), strerror(errno));
  return 0;
} // end member function Delete

// to tell if a sem exists
bool ETFShm::exist(const string &name){
  int fd = shm_open(name.data(), O_RDWR, 0);
  if(-1 == fd){
    if(ENOENT == errno){
      ETFMsg::Info("ETFShm", "exist: shared memory %s does not exist", name.data());
      return false;
    } // end if
    else ETFMsg::Error("ETFShm", "exist - %s: %s", name.c_str(), strerror(errno));
  } // end if
  return true;
} // end member function exist